
import { ExecutiveContext, State,Executive, Autherizable, ActivityStatus } from "../defination"
import { ActivityRecord, ByteArray, ExecutionRecord, ExecutiveRecord } from "../entity"
import {  ACTIVITY, ACTIVITY_STATUS, DEALER, EXECUTION_RECORD,  NEXT, OUTPUT, REQUEST, RESULT, STATE_RECORD } from "../shared-symbols"
import { BaseAction } from "./action"
import type { DealAction } from "./dealAction"
import { ExitAction } from "./exitAction"

export class DoneAction
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends BaseAction<TActivity,TState,TRecord,TContext>
{
  
  constructor(act:DealAction<TActivity,TState,TRecord,TContext>){
    super(act)
  }
  async invoke(context:TContext):Promise<BaseAction<TActivity,TState,TRecord,TContext>|undefined>{
    const activity = this[ACTIVITY]
    const currentStatus = activity[ACTIVITY_STATUS]
    if (currentStatus !== ActivityStatus.DEALING) {
      throw new Error('only can invoke enter action when the activity is dealing')
    }
    
    const at = context.calendar.now()
    const dealerId = context[DEALER].id
    const dealResult = this[RESULT]
    const dealReturnValue = dealResult?.dealReturnValue
    const outputBytes = dealReturnValue==undefined?undefined:context.serializer.serialize(dealReturnValue)
    const nextStatus = ActivityStatus.DONE
    const statusBytes = context.enumConverter.toByteArray<ByteArray>(nextStatus,ActivityStatus,true)!
    
    activity[ACTIVITY_STATUS] = nextStatus
    activity[OUTPUT] = dealReturnValue

    const activityRecord = this[STATE_RECORD] as unknown as ActivityRecord
    activityRecord.activityStatus = statusBytes
    activityRecord.output = outputBytes
    activityRecord.doneTime = at
    activityRecord.modifierId = dealerId
    activityRecord.updateTime = at

    const executionRecord = this[EXECUTION_RECORD] as ExecutionRecord
    executionRecord.activityStatus = statusBytes
    executionRecord.output = outputBytes
    executionRecord.updateTime = at

    await context.repository.doneActivity(
      activityRecord,
      executionRecord,
      context.enumConverter.toByteArray<ByteArray>(ActivityStatus.DEALING,ActivityStatus,true)!,
      context[REQUEST].requestId
    )
    return this.next!
  }

  get next():ExitAction<TActivity,TState,TRecord,TContext>| undefined{
    if (this[NEXT]===undefined){
      if (this[ACTIVITY][ACTIVITY_STATUS]===ActivityStatus.DONE){
        return this[NEXT] = new ExitAction(this)
      }
      return undefined
    }
    return this[NEXT] as ExitAction<TActivity,TState,TRecord,TContext> | undefined
  }
}